#include <iostream>
#include <vector>
#include <cstdio>
#include <cstring>
#include <string>
#include <unistd.h>
#include <pthread.h>
#include <memory>
#include "Mutex.hpp"
using namespace std;

// 共享资源 火车票
int tickets = 10000;
// 1. 如何看待锁
//    a. 锁 本身就是一个共享资源 全局的变量要被保护 锁是用来保护全局的资源的 锁本身也是全局资源 锁的安全谁来保护？
//    b. pthread_mutex_lock、pthread_mutex_unlock:加锁的过程必须是安全的 加锁的过程其实是原子的
//    c. 如果申请成功，就继续向后执行，如果申请暂时没有成功，执行流会阻塞
//    d. 谁持有锁 谁进入临界区
// 2. 如何理解加锁和解锁的本质 --加锁的过程是原子的
// 3. 如果我们想方便的使用 如何进行封装设计

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; // 静态初始化 编译时 无需手动销毁

// 1. 多个执行流进行安全访问的共享资源 - 临界资源
// 2. 我们把多个执行流中，访问临界资源的代码 -- 临界区 -- 往往是线程代码的很小的一部分
// 3. 想让多个线程串行访问共享资源 -- 互斥
// 4. 对一个资源进行访问的时候 要么不做 要么做完 -- 原子性 
// 一个对资源进行的操作，如果只用一条汇编就能完成 -- 原子性
// 反之: 不是原子的

// 解决方案：加锁

// 需要尽可能的让多个线程交叉执行
// 多个线程交叉执行本质: 就是让调度器尽可能的频繁发生线程调度与切换
// 线程何时发生切换： a.时间片到 b.来了更高优先级的线程 c.线程等待的时候
// 线程是在何时检测上面的情况呢？:从内核态返回用户态的时候 线程要对调度状态进行检测 如果可以 就直接发生线程切换

class ThreadData
{
public:
    ThreadData(const string &threadname, pthread_mutex_t *mutex_p) : threadname_(threadname), mutex_p_(mutex_p) {}
    ~ThreadData() {}

public:
    string threadname_;
    pthread_mutex_t *mutex_p_;
};

void *getTicket(void *args)
{
    
    string username = static_cast<const char *>(args);
    // ThreadData *td = static_cast<ThreadData *>(args);
    while (true)
    {
        // 加锁和解锁的过程多个线程串行执行的 程序变慢了
        // 锁只规定互斥访问 没有规定必须让谁优先执行
        // 锁就是真是的让多个执行流进行竞争的结果
        // pthread_mutex_lock(&lock); // 加锁
        {
            LockGuard lockguard(&lock); // RAII风格的加锁 确保资源在对象构造时获取 在析构时释放 从而避免资源泄漏
            if (tickets > 0)
            {
                usleep(1234); // 微秒 1秒 = 1^3毫秒 = 1^6微妙 = 10^9纳秒
                
                // cout << td->threadname_ << " 正在进行抢票: "<< tickets << endl;
                cout << username << " 正在进行抢票: " << tickets << endl;
                // 用usleep的时间来模拟真实的抢票要花费的时间
                tickets--;
                // pthread_mutex_unlock(&lock); // 解锁
            }
            else
            {
                // pthread_mutex_unlock(&lock); // 解锁
                break;
            }
        }

         usleep(1000); 
    }

    return nullptr;
}

int main()
{
    // #define NUM 4
    //     pthread_mutex_t lock;
    //     pthread_mutex_init(&lock, nullptr);
    //     vector<pthread_t> tids(NUM);
    //     for(int i = 0; i < NUM; i++)
    //     {
    //         char buffer[64];
    //         snprintf(buffer, sizeof(buffer), "thread %d", i+1);
    //         ThreadData *td = new ThreadData(buffer, &lock);
    //         pthread_create(&tids[i], nullptr, getTicket, td);
    //     }

    //     for(const auto &tid: tids)
    //     {
    //         pthread_join(tid, nullptr);
    //     }

    pthread_t t1, t2, t3, t4;
    pthread_create(&t1, nullptr, getTicket, (void *)"thread 1");
    pthread_create(&t2, nullptr, getTicket, (void *)"thread 2");
    pthread_create(&t3, nullptr, getTicket, (void *)"thread 3");
    pthread_create(&t4, nullptr, getTicket, (void *)"thread 4");

    pthread_join(t1, nullptr);
    pthread_join(t2, nullptr);
    pthread_join(t3, nullptr);
    pthread_join(t4, nullptr);
  
    return 0;
}
